package com.briup.day10;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/*
被abstract修饰的类，叫做抽象类，
   1、抽象类内部可以定义抽象方法
   2、抽象类不能被创建对象，但是抽象类还是能提供对应的构造器
抽象类无法创建对象，那为什么要提供构造器？
因为可以被子类调用，然后初始化属性
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public abstract class Animal {
    private String name;
    private int age;

    /*
    Animal的所有子类都对eat方法进行了重写；
    如果某一个方法必然会被子类重写，那么在父类中就
    无需编写该方法的实现逻辑，即无需编写该方法的方法体；
    但是去除掉方法体后，方法定义就报错了，在此时可以使用abstract关键字
    对该方法进行修饰；
    abstract修饰的抽象方法只能定义在抽象类，以及接口中，
    被abstract关键字修饰的抽象方法，必须被子类继承和重写；

    因为需要实现多态，所以哪怕某一个方法在父类中不实现，各个子类
    都必须编写的情况下，也必须将该方法定义在父类中
     */
    public abstract void eat();
}

class Tiger extends Animal {
    public Tiger(String name, int age) {
        // 在子类的构造器内，可以调用父类的构造器，访问定义在父类中的私有成员变量，
        // 创建子类对象时，可以给每一个对象开辟空间访问各自给父类设置的私有成员变量值
        super(name, age);
    }

    // 在tiger类中需要对eat方法进行重写
    public void eat() {
        System.out.println("老虎是肉食动物");
    }
}

class Panda extends Animal {
    public void eat() {
        System.out.println("熊猫爱吃竹子");
    }
}

// 如果子类没有对其继承父类的抽象方法进行重写/实现，
// 那么该子类必须定义为抽象类
abstract class Monkey extends Animal {
}

class Test {
    public static void main(String[] args) {
        invite(new Tiger("阿虎", 3));
        invite(new Panda());
    }

    // 假设动物界要举行大胃王争霸赛，要邀请所有的动物前来参赛，
    // 邀请时需要让这些动物都有进行吃饭表演
    private static void invite(Animal animal) {
        animal.eat();
    }
}
